Setup

library(biomaRt)
package ‘biomaRt’ was built under R version 3.6.3Registered S3 method overwritten by 'dplyr':
  method           from
  print.rowwise_df     
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
library(DESeq2)
Loading required package: S4Vectors
package ‘S4Vectors’ was built under R version 3.6.3Loading required package: stats4
Loading required package: BiocGenerics
Loading required package: parallel

Attaching package: ‘BiocGenerics’

The following objects are masked from ‘package:parallel’:

    clusterApply, clusterApplyLB, clusterCall, clusterEvalQ, clusterExport,
    clusterMap, parApply, parCapply, parLapply, parLapplyLB, parRapply, parSapply,
    parSapplyLB

The following objects are masked from ‘package:stats’:

    IQR, mad, sd, var, xtabs

The following objects are masked from ‘package:base’:

    anyDuplicated, append, as.data.frame, basename, cbind, colnames, dirname,
    do.call, duplicated, eval, evalq, Filter, Find, get, grep, grepl, intersect,
    is.unsorted, lapply, Map, mapply, match, mget, order, paste, pmax, pmax.int,
    pmin, pmin.int, Position, rank, rbind, Reduce, rownames, sapply, setdiff, sort,
    table, tapply, union, unique, unsplit, which, which.max, which.min


Attaching package: ‘S4Vectors’

The following object is masked from ‘package:base’:

    expand.grid

Loading required package: IRanges
Loading required package: GenomicRanges
Loading required package: GenomeInfoDb
package ‘GenomeInfoDb’ was built under R version 3.6.3Loading required package: SummarizedExperiment
Loading required package: Biobase
Welcome to Bioconductor

    Vignettes contain introductory material; view with 'browseVignettes()'. To cite
    Bioconductor, see 'citation("Biobase")', and for packages 'citation("pkgname")'.

Loading required package: DelayedArray
package ‘DelayedArray’ was built under R version 3.6.3Loading required package: matrixStats

Attaching package: ‘matrixStats’

The following objects are masked from ‘package:Biobase’:

    anyMissing, rowMedians

Loading required package: BiocParallel

Attaching package: ‘DelayedArray’

The following objects are masked from ‘package:matrixStats’:

    colMaxs, colMins, colRanges, rowMaxs, rowMins, rowRanges

The following objects are masked from ‘package:base’:

    aperm, apply, rowsum

Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
library(sleuth)
library(tximport)
package ‘tximport’ was built under R version 3.6.3
library(jsonlite)
library(data.table)
data.table 1.12.8 using 4 threads (see ?getDTthreads).  Latest news: r-datatable.com

Attaching package: ‘data.table’

The following object is masked from ‘package:SummarizedExperiment’:

    shift

The following object is masked from ‘package:GenomicRanges’:

    shift

The following object is masked from ‘package:IRanges’:

    shift

The following objects are masked from ‘package:S4Vectors’:

    first, second
library(vsn)
library(EnhancedVolcano)
Loading required package: ggplot2
Loading required package: ggrepel
library(viridis)
Loading required package: viridisLite
library(ggpubr)
Loading required package: magrittr
library(MASS)

Attaching package: ‘MASS’

The following object is masked from ‘package:biomaRt’:

    select
library(rtracklayer)
library(VennDiagram)
Loading required package: grid
Loading required package: futile.logger

Attaching package: ‘VennDiagram’

The following object is masked from ‘package:ggpubr’:

    rotate
library(RColorBrewer)
library(ComplexHeatmap)
========================================
ComplexHeatmap version 2.2.0
Bioconductor page: http://bioconductor.org/packages/ComplexHeatmap/
Github page: https://github.com/jokergoo/ComplexHeatmap
Documentation: http://jokergoo.github.io/ComplexHeatmap-reference

If you use it in published research, please cite:
Gu, Z. Complex heatmaps reveal patterns and correlations in multidimensional 
  genomic data. Bioinformatics 2016.
========================================
library(RColorBrewer)
library(circlize)
========================================
circlize version 0.4.8
CRAN page: https://cran.r-project.org/package=circlize
Github page: https://github.com/jokergoo/circlize
Documentation: http://jokergoo.github.io/circlize_book/book/

If you use it in published research, please cite:
Gu, Z. circlize implements and enhances circular visualization 
  in R. Bioinformatics 2014.
========================================
library(tidyverse)
── Attaching packages ────────────────────────────────────────────────────── tidyverse 1.3.0 ──
✓ tibble  3.0.1     ✓ dplyr   0.8.5
✓ tidyr   1.0.2     ✓ stringr 1.4.0
✓ readr   1.3.1     ✓ forcats 0.5.0
✓ purrr   0.3.4     
── Conflicts ───────────────────────────────────────────────────────── tidyverse_conflicts() ──
x dplyr::between()    masks data.table::between()
x dplyr::collapse()   masks IRanges::collapse()
x dplyr::combine()    masks Biobase::combine(), BiocGenerics::combine()
x dplyr::count()      masks matrixStats::count()
x dplyr::desc()       masks IRanges::desc()
x tidyr::expand()     masks S4Vectors::expand()
x tidyr::extract()    masks magrittr::extract()
x dplyr::filter()     masks stats::filter()
x dplyr::first()      masks data.table::first(), S4Vectors::first()
x purrr::flatten()    masks jsonlite::flatten()
x dplyr::lag()        masks stats::lag()
x dplyr::last()       masks data.table::last()
x ggplot2::Position() masks BiocGenerics::Position(), base::Position()
x purrr::reduce()     masks GenomicRanges::reduce(), IRanges::reduce()
x dplyr::rename()     masks S4Vectors::rename()
x dplyr::select()     masks MASS::select(), biomaRt::select()
x purrr::set_names()  masks magrittr::set_names()
x purrr::simplify()   masks DelayedArray::simplify()
x dplyr::slice()      masks IRanges::slice()
x purrr::transpose()  masks data.table::transpose()
set.seed(42)
RESPONSE_COLORS = c("NR"="purple","R"="orange")

Setup sample details

# Load in samples and point to kallisto paths
samples <- read_tsv("../work/input/sample_manifest.tsv")
Parsed with column specification:
cols(
  `Patient ID` = col_character(),
  `GSM ID` = col_character(),
  Response = col_character(),
  SRR = col_character()
)
samples$kallisto_path <- file.path("..","work","intermediate","quant",samples$SRR)

Transcript <-> Gene Mappings

mart <- biomaRt::useMart(biomart = "ENSEMBL_MART_ENSEMBL",
  dataset = "hsapiens_gene_ensembl",
  host = 'grch37.ensembl.org')
gene_features <- biomaRt::getBM(attributes = c("ensembl_transcript_id_version", "ensembl_gene_id",
    "external_gene_name","transcript_length"), mart = mart)
Cache found
t2g <- gene_features %>%
  select(ensembl_transcript_id_version,ensembl_gene_id,external_gene_name)
avg_tx_len <-gene_features %>%
  group_by(external_gene_name) %>%
  summarise(length=mean(transcript_length))

QC Filtering

We’ll filter high quality samples by percent reads mapped by Kallisto. We’ll need to get hte total reads in files from the kallisto logs, and the total number of mapped reads by pre-loading all the counts using sleuth.

# Find total reads to compute percent mapped
samples$n_reads <- apply(samples,1,function(x)read_json(file.path(x["kallisto_path"],"run_info.json"))$n_processed)
# Initial sleuth just to get kallisto counts
s_obj_counts <- sleuth_prep(
  sample_to_covariates = rename(samples,path=kallisto_path,sample=SRR),
  normalize=FALSE
)
It appears that you are running Sleuth from within Rstudio.
Because of concerns with forking processes from a GUI, 'num_cores' is being set to 1.
If you wish to take advantage of multiple cores, please consider running sleuth from the command line.reading in kallisto results
dropping unused factor levels
................
86131 targets passed the filter
kallisto_est_mapped <- s_obj_counts$obs_raw %>%
  group_by(sample) %>%
  summarise(kallisto_mapped_reads=sum(est_counts))
samples <- samples %>% 
  left_join(kallisto_est_mapped,by=c("SRR"="sample")) %>%
  mutate(percent_mapped=kallisto_mapped_reads/n_reads) 
samples %>%
  ggplot(aes(x = percent_mapped)) +
    geom_histogram(bins = 10) +
    labs(title = "Distribution of % Mapped Reads", x = "% Mapped Reads")

# Filter samples
samples_filt <-samples %>% 
  filter(
    !SRR %in% c("SRR8526726"),
    #percent_mapped > 0.8
    )

Sleuth

PCA

Sleuth default

plot_pca(s_obj,units="scaled_reads_per_base",color_by = "Response",use_filtered = TRUE,text_labels = F) + geom_label(aes(label=sample),size=2)

Standardized PCA


sleuth_rna_norm <- s_obj$obs_norm_filt %>% 
  select(sample,target_id,scaled_reads_per_base) %>%
  spread(key=target_id,value=scaled_reads_per_base) %>%
  column_to_rownames("sample")
sleuth_std_pca <- prcomp((sleuth_rna_norm),center=T,scale.=T)
sleuth_variance_explained = summary(sleuth_std_pca)$importance[2,1:2]
sleuth_std_pca$x[,1:2] %>%
  as.data.frame() %>%
  rownames_to_column("SRR") %>%
  left_join(samples,by="SRR") %>%
  ggplot(aes(x=PC1,y=PC2,color=Response,label=SRR)) +
    geom_point(size=3) +
    labs(title="PCA Plot of Sleuth Normalized Kallisto Quantified Data",
         x = paste0("PC1: ", round(sleuth_variance_explained[1]*100),"% Variance"),
         y = paste0("PC2: ", round(sleuth_variance_explained[2]*100),"% Variance")) +
    scale_color_manual(values=RESPONSE_COLORS)

Diffex

LRT

s_obj <- s_obj %>%
  sleuth_fit(~Response,"full") %>%
  sleuth_fit(~1,"reduced") %>%
  sleuth_lrt("reduced","full")
fitting measurement error models
shrinkage estimation
computing variance of betas
fitting measurement error models
shrinkage estimation
computing variance of betas
sleuth_diffex_lrt <- sleuth_results(s_obj,test="reduced:full",test_type="lrt") %>% filter(!is.na(pval))
sleuth_diffex_lrt %>% arrange(qval)

Wald

s_obj <- sleuth_wt(s_obj,"ResponseR")
sleuth_diffex_wald <- sleuth_results(s_obj,test="ResponseR",test_type="wald")  %>% filter(!is.na(pval))
sleuth_diffex_wald %>% arrange(qval) 
plot_volcano(s_obj,test_type="wt",test="ResponseR",sig_level=0.05) +
  labs(title="Volcano Plot of Sleuth Identified Differentially Expressed Genes",
       x="log2FC", y = "qval",
        color="Q < 0.05")

QC

meanSdPlot(log2(sleuth_to_matrix(s_obj,"obs_norm","scaled_reads_per_base") + 1),rank=FALSE,bins=100)

DESeq (Kallisto)

files <- file.path(samples_filt$kallisto_path,"abundance.h5")
names(files) <- samples_filt$`Patient ID`
txi <- tximport(
  file.path(samples_filt$kallisto_path,"abundance.h5"),
  type = "kallisto",
  tx2gene = select(
    t2g,
    TXNAME = ensembl_transcript_id_version,
    GENEID = external_gene_name
  ),
  ignoreAfterBar = TRUE,
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 
summarizing abundance
summarizing counts
summarizing length
summarizing inferential replicates
rownames(samples_filt) <- samples_filt$`Patient ID`
Setting row names on a tibble is deprecated.
deseq_kallisto_data <- DESeqDataSetFromTximport(txi,samples_filt,~Response)
some variables in design formula are characters, converting to factorsusing counts and average transcript lengths from tximport
kallisto_keep <- rowSums(counts(deseq_kallisto_data)>5)>5
deseq_kallisto_data <- deseq_kallisto_data[kallisto_keep,]

QC

deseq_kallisto_norm <- vst(deseq_kallisto_data)
using 'avgTxLength' from assays(dds), correcting for library size
meanSdPlot(assay(deseq_kallisto_norm),ranks=FALSE,bins=100)

PCA

plotPCA(deseq_kallisto_norm,intgroup="Response",ntop=500000)   +
  labs(title="PCA Plot of DESeq Normalized Kallisto Quantified Data",
       color="Response") +
  scale_color_manual(values=RESPONSE_COLORS)

Difex

deseq_kallisto_data <- DESeq(deseq_kallisto_data)
estimating size factors
using 'avgTxLength' from assays(dds), correcting for library size
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
-- replacing outliers and refitting for 1306 genes
-- DESeq argument 'minReplicatesForReplace' = 7 
-- original counts are preserved in counts(dds)
estimating dispersions
fitting model and testing
deseq_kallisto_lrt <- results(deseq_kallisto_data,name="Response_R_vs_NR",tidy=TRUE)
deseq_kallisto_lrt %>% 
  #filter(padj < 0.05) %>%
  arrange(padj) %>%
  select(row,log2FoldChange,pvalue,padj) %>%
  left_join(select(t2g,ensembl_gene_id,external_gene_name),by=c("row"="ensembl_gene_id"))

DESeq (FeatureCounts)

fc_mat <- read_tsv("../work/input/GSE126044_counts.txt")
Missing column names filled in: 'X1' [1]Parsed with column specification:
cols(
  X1 = col_character(),
  Dis_01 = col_double(),
  Dis_02 = col_double(),
  Dis_11 = col_double(),
  Dis_12 = col_double(),
  Dis_15 = col_double(),
  Dis_04 = col_double(),
  Dis_16 = col_double(),
  Dis_17 = col_double(),
  Dis_03 = col_double(),
  Dis_07 = col_double(),
  Dis_10 = col_double(),
  Dis_18 = col_double(),
  Dis_09 = col_double(),
  Dis_05 = col_double(),
  Dis_06 = col_double(),
  Dis_08 = col_double()
)
 fc_mat <- fc_mat[!grepl("^\\d+\\-",fc_mat$X1),] %>%
   column_to_rownames("X1")
 
fc_mat_filt <-  fc_mat %>%
   select(one_of(samples_filt$`Patient ID`))
deseq_fc <- DESeqDataSetFromMatrix(countData=fc_mat_filt,colData=samples_filt,design=~Response)
converting counts to integer mode
some variables in design formula are characters, converting to factors
fc_keep <- rowSums(counts(deseq_fc)>5)>5
deseq_fc <- deseq_fc[fc_keep,]

QC

deseq_fc_norm <- vst(deseq_fc)
meanSdPlot(assay(deseq_fc_norm),rank=FALSE,bins=100)

PCA

plotPCA(deseq_fc_norm,intgroup="Response",ntop=500000) +
  labs(title="PCA Plot of DESeq Normalized Feature Counts/STAR Quantified Data",
       color="Response") +
  scale_color_manual(values=RESPONSE_COLORS)

Difex

Merged Analyses

counts_to_rpm, pca

tfc <- t(fc_mat_filt)
# Convert Kallisto counts to rpm
kalread<-cbind(kallisto_mapped_reads=samples_filt$kallisto_mapped_reads, sleuth_rna_norm %>% rownames_to_column("sample"))
kalrpm<-setDT(kalread) [, lapply(.SD, function(x) (x * 1e6) / (kallisto_mapped_reads)),  by=.(kallisto_mapped_reads,sample)] %>% 
  column_to_rownames("sample")

# Convert star fc to rpm
#get df with est total reads for star
staread<-cbind(star_est_mapped=colSums(fc_mat_filt), as.data.frame(tfc) %>% rownames_to_column("sample"))
starpm<-setDT(staread) [, lapply(.SD, function(x) (x * 1e6) / (star_est_mapped)),  by=.(star_est_mapped,sample)] %>%
  column_to_rownames("sample")

## scale pca with rpm
kal_rpm_pca <- prcomp(kalrpm[,-1],center=TRUE,scale.=TRUE)

starnorm_std <- scale(starpm[,-1])
#star counts include some zero, set scale/std apply to those !=0
star_rpm_pca <- prcomp(starnorm_std[ , which(apply(starnorm_std, 2, var) != 0)],center=TRUE)

## superimposed pca plots from the two sources
p1 <-kal_rpm_pca$x[,1:2]%>%
  as.data.frame() %>%
  rownames_to_column("SRR") %>%
  left_join(samples_filt,by="SRR")
p2<-star_rpm_pca$x[,1:2] %>%
  as.data.frame() %>%
  rownames_to_column("Patient ID") %>%
  left_join(samples_filt,by="Patient ID")
bind_rows(Kallisto=p1,STAR=p2,.id="quant_type") %>%
  ggplot(aes(x=PC1,y=PC2,color=Response,shape=quant_type)) +
    geom_point(size=3) +
    scale_color_manual(values=RESPONSE_COLORS) + 
    labs(title="Superimposed PCA of RPM Normalized Counts",
         shape="Method")

Comparison of log2FC

equation = function(x) {
  lm_coef <- list(a = round(as.numeric(coef(x)[1]), digits = 2),
                  b = round(as.numeric(coef(x)[2]), digits = 2),
                  r2 = round(summary(x)$r.squared, digits = 2));
  lm_eq <- substitute(italic(y) == a + b %.% italic(x)*","~~italic(R)^2~"="~r2,lm_coef)
  as.character(as.expression(lm_eq));                 
}

log2fc_combined <- sleuth_diffex_wald %>%
  filter(!is.na(b)) %>%
  select(gene=target_id,sleuth_log2FC=b,sleuth_q=qval) %>%
  left_join(select(deseq_kallisto_lrt,row,deseq_kal_log2FC=log2FoldChange,deseq_kal_q=padj),by=c("gene"="row")) %>%
  left_join(select(deseq_fc_lrt,row,deseq_fc_log2FC=log2FoldChange,deseq_fc_q=padj),by=c("gene"="row")) %>%
  filter(!is.na(deseq_fc_log2FC))

regression.line <- lm(deseq_kal_log2FC ~ sleuth_log2FC, data = log2fc_combined)
log2fc_combined %>%
  ggplot(aes(x=sleuth_log2FC,y=deseq_kal_log2FC)) +
  geom_point(aes(x=sleuth_log2FC,y=deseq_kal_log2FC)) +
  geom_hline(yintercept=0, color="darkgray") + 
  geom_vline(xintercept=0, color="darkgray") +
  theme_pubr(base_size = 14)+
  geom_smooth(method = "lm", color="red") +
  annotate("text", label = equation(regression.line), x = -3, y = 4, parse = TRUE)+
  labs(title="Comparison of log2FC between Sleuth and DESeq Kallisto",
       x="log2 Fold Change (Sleuth-Kallisto)",y="log2 Fold Change (DESeq-Kallisto)")



regression.line <- lm(deseq_fc_log2FC ~ deseq_kal_log2FC, data = log2fc_combined)
log2fc_combined %>%
  ggplot(aes(x=deseq_kal_log2FC,y=deseq_fc_log2FC)) +
  geom_point() +
  geom_hline(yintercept=0, color="darkgray") + 
  geom_vline(xintercept=0, color="darkgray") +
  theme_pubr(base_size = 14)+
  geom_smooth(method = "lm", color="red") +
  annotate("text", label = equation(regression.line), x = -4, y = 5, parse = TRUE)+
  labs(title="Comparison of log2FC between DESeq From Kallisto and FC",
       x="log2 Fold Change (DESeq-FeatureCounts)",y="log2 Fold Change (DESeq-Kallisto)")

combine all logFC data

sleuth_diffex_wald <- sleuth_diffex_wald %>% filter(!is.na(b)) 
deseq_fc_lrt <- deseq_fc_lrt %>% filter(!is.na(log2FoldChange)) 
deseq_kallisto_lrt <- deseq_kallisto_lrt %>% filter(!is.na(log2FoldChange)) 

log2fc_full_combined <- sleuth_diffex_wald %>%
  select(gene=target_id,sleuth_log2FC=b,sleuth_q=qval) %>%
  full_join(select(deseq_kallisto_lrt,row,deseq_kal_log2FC=log2FoldChange,deseq_kal_q=padj),by=c("gene"="row")) %>%
  full_join(select(deseq_fc_lrt,row,deseq_fc_log2FC=log2FoldChange,deseq_fc_q=padj),by=c("gene"="row"))
log2fc_full_combined
NA

Volcano Plots

Slueth


log2fc_full_combined$log2FoldChange <- log2fc_full_combined$sleuth_log2FC
log2fc_full_combined$padj <- log2fc_full_combined$sleuth_q
log2fc_final <- log2fc_full_combined %>% filter(!is.na(log2FoldChange)) 

thres <- 1
keyvals.color <- rep('green', nrow(log2fc_final))
# set the base name/label as 'Mid'
names(keyvals.color) <- rep('Mid', nrow(log2fc_final))

keyvals.color[which(log2fc_final$log2FoldChange > thres)] <- 'red'
names(keyvals.color)[which(log2fc_final$log2FoldChange > thres)] <- 'UpRegulated'

keyvals.color[which(log2fc_final$log2FoldChange < -thres)] <- 'royalblue'
names(keyvals.color)[which(log2fc_final$log2FoldChange < -thres)] <- 'DownRegulated'

down.genes  <- which((log2fc_final$padj < 0.3) & (log2fc_final$log2FoldChange <= -3))
up.genes  <- which((log2fc_final$padj < 0.3) & (log2fc_final$log2FoldChange >= 3))
up.down.genes = c(down.genes, up.genes)
length(up.down.genes)
[1] 27
table(keyvals.color)
keyvals.color
    green       red royalblue 
    16210      1794      1338 
EnhancedVolcano(log2fc_final,
                lab = (log2fc_final$gene),
                x = 'log2FoldChange',
                y = 'padj', 
                title = 'R vs. NR  - sleuth',
                drawConnectors = TRUE,
                widthConnectors = 0.3,
                colConnectors = 'grey30',
                colCustom = keyvals.color,
                pCutoff = NA,
                cutoffLineType = 'blank',
                vline = c(-thres,thres),
                vlineCol = c('grey50', 'grey50'),
                gridlines.major = FALSE,
                gridlines.minor = FALSE,
                ylim = c(0,1.5),
                selectLab = log2fc_final$gene[up.down.genes]
                )

deseq_kal


log2fc_full_combined$log2FoldChange <- log2fc_full_combined$deseq_kal_log2FC
log2fc_full_combined$padj <- log2fc_full_combined$deseq_kal_q
log2fc_final <- log2fc_full_combined %>% filter(!is.na(log2FoldChange)) 

thres <- 1
keyvals.color <- rep('green', nrow(log2fc_final))
# set the base name/label as 'Mid'
names(keyvals.color) <- rep('Mid', nrow(log2fc_final))

keyvals.color[which(log2fc_final$log2FoldChange > thres)] <- 'red'
names(keyvals.color)[which(log2fc_final$log2FoldChange > thres)] <- 'UpRegulated'

keyvals.color[which(log2fc_final$log2FoldChange < -thres)] <- 'royalblue'
names(keyvals.color)[which(log2fc_final$log2FoldChange < -thres)] <- 'DownRegulated'

down.genes  <- which((log2fc_final$padj < 0.3) & (log2fc_final$log2FoldChange <= -20))
up.genes  <- which((log2fc_final$padj < 0.3) & (log2fc_final$log2FoldChange >= 5))
up.down.genes = c(down.genes, up.genes)
length(up.down.genes)
[1] 6
table(keyvals.color)
keyvals.color
    green       red royalblue 
    16311      2059      2369 
EnhancedVolcano(log2fc_final,
                lab = (log2fc_final$gene),
                x = 'log2FoldChange',
                y = 'padj', 
                title = 'R vs. NR  - Deseq-kallisto',
                drawConnectors = TRUE,
                widthConnectors = 0.3,
                colConnectors = 'grey30',
                colCustom = keyvals.color,
                pCutoff = NA,
                cutoffLineType = 'blank',
                vline = c(-thres,thres),
                vlineCol = c('grey50', 'grey50'),
                gridlines.major = FALSE,
                gridlines.minor = FALSE,
                ylim = c(0,10),
                selectLab = log2fc_final$gene[up.down.genes]
                )

deseq_fc

log2fc_full_combined$log2FoldChange <- log2fc_full_combined$deseq_fc_log2FC
log2fc_full_combined$padj <- log2fc_full_combined$deseq_fc_q
log2fc_final <- log2fc_full_combined %>% filter(!is.na(log2FoldChange)) 

thres <- 1
keyvals.color <- rep('green', nrow(log2fc_final))
# set the base name/label as 'Mid'
names(keyvals.color) <- rep('Mid', nrow(log2fc_final))

keyvals.color[which(log2fc_final$log2FoldChange > thres)] <- 'red'
names(keyvals.color)[which(log2fc_final$log2FoldChange > thres)] <- 'UpRegulated'

keyvals.color[which(log2fc_final$log2FoldChange < -thres)] <- 'royalblue'
names(keyvals.color)[which(log2fc_final$log2FoldChange < -thres)] <- 'DownRegulated'

down.genes  <- which((log2fc_final$padj < 0.3) & (log2fc_final$log2FoldChange <= -8))
up.genes  <- which((log2fc_final$padj < 0.3) & (log2fc_final$log2FoldChange >= 4))
up.down.genes = c(down.genes, up.genes)
length(up.down.genes)
[1] 9
table(keyvals.color)
keyvals.color
    green       red royalblue 
    12879      1621      1650 
EnhancedVolcano(log2fc_final,
                lab = (log2fc_final$gene),
                x = 'log2FoldChange',
                y = 'padj', 
                title = 'R vs. NR  - Deseq-fc',
                drawConnectors = TRUE,
                widthConnectors = 0.3,
                colConnectors = 'grey30',
                colCustom = keyvals.color,
                pCutoff = NA,
                cutoffLineType = 'blank',
                vline = c(-thres,thres),
                vlineCol = c('grey50', 'grey50'),
                gridlines.major = FALSE,
                gridlines.minor = FALSE,
                ylim = c(0,15),
                selectLab = log2fc_final$gene[up.down.genes]
                )

Venn diagram

thres <- 1
# get the name of the upregulated/downregulated genes from 3 different methods
sleuth.up <- sleuth_diffex_wald$target_id[which((sleuth_diffex_wald$qval <= 0.05) & (sleuth_diffex_wald$b >= thres))]
sleuth.down <- sleuth_diffex_wald$target_id[which((sleuth_diffex_wald$qval <= 0.05) & (sleuth_diffex_wald$b <= -thres))]
deseq.k.up <- deseq_kallisto_lrt$row[which((deseq_kallisto_lrt$padj <= 0.05) & (deseq_kallisto_lrt$log2FoldChange >= thres))]
deseq.k.down <- deseq_kallisto_lrt$row[which((deseq_kallisto_lrt$padj <= 0.05) & (deseq_kallisto_lrt$log2FoldChange <= -thres))]
deseq.fc.up <- deseq_fc_lrt$row[which((deseq_fc_lrt$padj <= 0.05) & (deseq_fc_lrt$log2FoldChange >= thres))]
deseq.fc.down <- deseq_fc_lrt$row[which((deseq_fc_lrt$padj <= 0.05) & (deseq_fc_lrt$log2FoldChange <= -thres))]

# up-regulated venn-diagram
grid.newpage()
vd <- venn.diagram(x = list("Sleuth-Kallisto" = (sleuth.up),
                            "DESeq-Kallisto" = (deseq.k.up),
                            "DESeq-FeatureCounts" = (deseq.fc.up)),
                    main="Venn Diagram of Upregulated Genes Between 3 Methods",
                   fill = brewer.pal(4, "Set2")[1:3], filename = NULL)
grid.draw(vd)


# down-regulated venn-diagram
grid.newpage()
vd <- venn.diagram(x = list("Sleuth-Kallisto" = (sleuth.down),
                            "DESeq-Kallisto" = (deseq.k.down),
                            "DESeq-FeatureCounts" = (deseq.fc.down)),
                   main="Venn Diagram of Downregulated Genes Between 3 Methods",
                   fill = brewer.pal(4, "Set2")[1:3], filename = NULL)
grid.draw(vd)

Heatmap


#sleuth.up <- sleuth_diffex_wald$target_id[which((sleuth_diffex_wald$qval <= 0.05) & (sleuth_diffex_wald$b >= thres))]
#sleuth.down <- sleuth_diffex_wald$target_id[which((sleuth_diffex_wald$qval <= 0.05) & (sleuth_diffex_wald$b <= -thres))]

normalized_counts <- counts(deseq_kallisto_data, normalized=TRUE)
colnames(normalized_counts)
 [1] "Dis_01" "Dis_02" "Dis_03" "Dis_04" "Dis_05" "Dis_06" "Dis_07" "Dis_08" "Dis_09" "Dis_11"
[11] "Dis_12" "Dis_15" "Dis_16" "Dis_17" "Dis_18"
updown.gene <- deseq_kallisto_lrt[which((deseq_kallisto_lrt$log2FoldChange > 1 | deseq_kallisto_lrt$log2FoldChange < -1) & deseq_kallisto_lrt$padj < 0.05),]
updown.gene$updown <- ifelse((updown.gene$log2FoldChange> 1), "up", "down")

updown.regul <- as.matrix(updown.gene[,"updown", drop=FALSE])

heatmapdata <- normalized_counts[match(updown.gene$row, row.names(normalized_counts)),]

zscore_data <- t(apply(heatmapdata, 1, scale))
colnames(zscore_data) = colnames(heatmapdata)

col_updown = c("up" = "pink", "down" = "purple")

row.names(samples_filt) <- samples_filt$`Patient ID`
Setting row names on a tibble is deprecated.
samples_filt

ha = HeatmapAnnotation(type = samples_filt$Response, annotation_name_side = "left",  
                       col = list(type = c("NR" = "yellow", "R" = "black")))
dim(normalized_counts)
[1] 20739    15
ht1 = Heatmap(zscore_data, name = "expression",
        col = colorRamp2(c(-2, 0, 2), c("blue", "white", "red")),
        show_column_names = FALSE, show_row_names=FALSE,
        row_title = NULL,
        bottom_annotation = ha,
        heatmap_width = 400, heatmap_height = 100)

ht2 = Heatmap(updown.regul, name = "up vs down", col = col_updown, show_row_names=FALSE)
ht_list = ht1 + ht2
draw(ht_list, row_km = 1, row_split = updown.regul, cluster_rows = TRUE)


#sleuth.up <- sleuth_diffex_wald$target_id[which((sleuth_diffex_wald$qval <= 0.05) & (sleuth_diffex_wald$b >= thres))]
#sleuth.down <- sleuth_diffex_wald$target_id[which((sleuth_diffex_wald$qval <= 0.05) & (sleuth_diffex_wald$b <= -thres))]
normalized_counts <- counts(deseq_fc, normalized=TRUE)

updown.gene <- deseq_fc_lrt[which((deseq_fc_lrt$log2FoldChange > 1 | deseq_fc_lrt$log2FoldChange < -1) & deseq_fc_lrt$padj < 0.05),]
updown.gene$updown <- ifelse((updown.gene$log2FoldChange> 1), "up", "down")

updown.regul <- as.matrix(updown.gene[,"updown", drop=FALSE])

heatmapdata <- normalized_counts[match(updown.gene$row, row.names(normalized_counts)),]

zscore_data <- t(apply(heatmapdata, 1, scale))
colnames(zscore_data) = colnames(heatmapdata)

col_updown = c("up" = "pink", "down" = "purple")

row.names(samples_filt) <- samples_filt$`Patient ID`
Setting row names on a tibble is deprecated.
samples_filt

ha = HeatmapAnnotation(type = samples_filt$Response, annotation_name_side = "left",  
                       col = list(type = c("NR" = "yellow", "R" = "black")))
dim(normalized_counts)
[1] 16150    15
ht1 = Heatmap(zscore_data, name = "expression",
        col = colorRamp2(c(-2, 0, 2), c("blue", "white", "red")),
        show_column_names = FALSE, show_row_names=FALSE,
        row_title = NULL,
        bottom_annotation = ha,
        heatmap_width = 400, heatmap_height = 100)

ht2 = Heatmap(updown.regul, name = "up vs down", col = col_updown, show_row_names=FALSE)
ht_list = ht1 + ht2
draw(ht_list, row_km = 1, row_split = updown.regul, cluster_rows = TRUE)

Sample overview table

sample_summary <- data.frame(fc_mapped_reads=colSums(fc_mat)) %>% 
  rownames_to_column("Patient ID") %>% 
  left_join(samples,by="Patient ID") %>%
  select(`Patient ID`,SRR,Response,"Sequenced Reads"=n_reads,"Mapped Reads(Kallisto)"=kallisto_mapped_reads,"Mapped Reads(STAR/FeatureCounts)"=fc_mapped_reads)
sample_summary

Write out relevant files

sleuth_diffex_lrt %>% write_tsv("sleuth_diffex_lrt.tsv")
sleuth_diffex_wald %>% write_tsv("sleuth_diffex_wald.tsv")
deseq_kallisto_lrt %>% write_tsv("deseq_kallisto_lrt.tsv")
deseq_fc_lrt %>% write_tsv("deseq_fc_lrt.tsv")
sample_summary%>% write_tsv("sample_summary.tsv")
LS0tCnRpdGxlOiAiQ29tcGFyaXNvbnMgb2YgRGlmZmVyZW50aWFsIEV4cHJlc3Npb24gVG9vbHMgb24gRGlmZmVyZW50IERhdGEgU291cmNlcyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyBTZXR1cApgYGB7cn0KbGlicmFyeShiaW9tYVJ0KQpsaWJyYXJ5KERFU2VxMikKbGlicmFyeShzbGV1dGgpCmxpYnJhcnkodHhpbXBvcnQpCmxpYnJhcnkoanNvbmxpdGUpCmxpYnJhcnkoZGF0YS50YWJsZSkKbGlicmFyeSh2c24pCmxpYnJhcnkoRW5oYW5jZWRWb2xjYW5vKQpsaWJyYXJ5KHZpcmlkaXMpCmxpYnJhcnkoZ2dwdWJyKQpsaWJyYXJ5KE1BU1MpCmxpYnJhcnkocnRyYWNrbGF5ZXIpCmxpYnJhcnkoVmVubkRpYWdyYW0pCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeShjaXJjbGl6ZSkKbGlicmFyeSh0aWR5dmVyc2UpCnNldC5zZWVkKDQyKQpSRVNQT05TRV9DT0xPUlMgPSBjKCJOUiI9InB1cnBsZSIsIlIiPSJvcmFuZ2UiKQpgYGAKCiMjIFNldHVwIHNhbXBsZSBkZXRhaWxzCmBgYHtyfQojIExvYWQgaW4gc2FtcGxlcyBhbmQgcG9pbnQgdG8ga2FsbGlzdG8gcGF0aHMKc2FtcGxlcyA8LSByZWFkX3RzdigiLi4vd29yay9pbnB1dC9zYW1wbGVfbWFuaWZlc3QudHN2IikKc2FtcGxlcyRrYWxsaXN0b19wYXRoIDwtIGZpbGUucGF0aCgiLi4iLCJ3b3JrIiwiaW50ZXJtZWRpYXRlIiwicXVhbnQiLHNhbXBsZXMkU1JSKQpgYGAKCiMjIFRyYW5zY3JpcHQgPC0+IEdlbmUgTWFwcGluZ3MKYGBge3J9Cm1hcnQgPC0gYmlvbWFSdDo6dXNlTWFydChiaW9tYXJ0ID0gIkVOU0VNQkxfTUFSVF9FTlNFTUJMIiwKICBkYXRhc2V0ID0gImhzYXBpZW5zX2dlbmVfZW5zZW1ibCIsCiAgaG9zdCA9ICdncmNoMzcuZW5zZW1ibC5vcmcnKQpnZW5lX2ZlYXR1cmVzIDwtIGJpb21hUnQ6OmdldEJNKGF0dHJpYnV0ZXMgPSBjKCJlbnNlbWJsX3RyYW5zY3JpcHRfaWRfdmVyc2lvbiIsICJlbnNlbWJsX2dlbmVfaWQiLAogICAgImV4dGVybmFsX2dlbmVfbmFtZSIsInRyYW5zY3JpcHRfbGVuZ3RoIiksIG1hcnQgPSBtYXJ0KQp0MmcgPC0gZ2VuZV9mZWF0dXJlcyAlPiUKICBzZWxlY3QoZW5zZW1ibF90cmFuc2NyaXB0X2lkX3ZlcnNpb24sZW5zZW1ibF9nZW5lX2lkLGV4dGVybmFsX2dlbmVfbmFtZSkKYXZnX3R4X2xlbiA8LWdlbmVfZmVhdHVyZXMgJT4lCiAgZ3JvdXBfYnkoZXh0ZXJuYWxfZ2VuZV9uYW1lKSAlPiUKICBzdW1tYXJpc2UobGVuZ3RoPW1lYW4odHJhbnNjcmlwdF9sZW5ndGgpKQpgYGAKCiMjIFFDIEZpbHRlcmluZwpXZSdsbCBmaWx0ZXIgaGlnaCBxdWFsaXR5IHNhbXBsZXMgYnkgcGVyY2VudCByZWFkcyBtYXBwZWQgYnkgS2FsbGlzdG8uIFdlJ2xsIG5lZWQgdG8gZ2V0IGh0ZSB0b3RhbCByZWFkcyBpbiBmaWxlcyBmcm9tIHRoZSBrYWxsaXN0byBsb2dzLCBhbmQgdGhlIHRvdGFsIG51bWJlciBvZiBtYXBwZWQgcmVhZHMgYnkgcHJlLWxvYWRpbmcgYWxsIHRoZSBjb3VudHMgdXNpbmcgc2xldXRoLiAKYGBge3J9CiMgRmluZCB0b3RhbCByZWFkcyB0byBjb21wdXRlIHBlcmNlbnQgbWFwcGVkCnNhbXBsZXMkbl9yZWFkcyA8LSBhcHBseShzYW1wbGVzLDEsZnVuY3Rpb24oeClyZWFkX2pzb24oZmlsZS5wYXRoKHhbImthbGxpc3RvX3BhdGgiXSwicnVuX2luZm8uanNvbiIpKSRuX3Byb2Nlc3NlZCkKYGBgCgpgYGB7cn0KIyBJbml0aWFsIHNsZXV0aCBqdXN0IHRvIGdldCBrYWxsaXN0byBjb3VudHMKc19vYmpfY291bnRzIDwtIHNsZXV0aF9wcmVwKAogIHNhbXBsZV90b19jb3ZhcmlhdGVzID0gcmVuYW1lKHNhbXBsZXMscGF0aD1rYWxsaXN0b19wYXRoLHNhbXBsZT1TUlIpLAogIG5vcm1hbGl6ZT1GQUxTRQopCmBgYAoKYGBge3J9CmthbGxpc3RvX2VzdF9tYXBwZWQgPC0gc19vYmpfY291bnRzJG9ic19yYXcgJT4lCiAgZ3JvdXBfYnkoc2FtcGxlKSAlPiUKICBzdW1tYXJpc2Uoa2FsbGlzdG9fbWFwcGVkX3JlYWRzPXN1bShlc3RfY291bnRzKSkKc2FtcGxlcyA8LSBzYW1wbGVzICU+JSAKICBsZWZ0X2pvaW4oa2FsbGlzdG9fZXN0X21hcHBlZCxieT1jKCJTUlIiPSJzYW1wbGUiKSkgJT4lCiAgbXV0YXRlKHBlcmNlbnRfbWFwcGVkPWthbGxpc3RvX21hcHBlZF9yZWFkcy9uX3JlYWRzKSAKc2FtcGxlcyAlPiUKICBnZ3Bsb3QoYWVzKHggPSBwZXJjZW50X21hcHBlZCkpICsKICAgIGdlb21faGlzdG9ncmFtKGJpbnMgPSAxMCkgKwogICAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgJSBNYXBwZWQgUmVhZHMiLCB4ID0gIiUgTWFwcGVkIFJlYWRzIikKYGBgCgpgYGB7cn0KIyBGaWx0ZXIgc2FtcGxlcwpzYW1wbGVzX2ZpbHQgPC1zYW1wbGVzICU+JSAKICBmaWx0ZXIoCiAgICAhU1JSICVpbiUgYygiU1JSODUyNjcyNiIpLAogICAgI3BlcmNlbnRfbWFwcGVkID4gMC44CiAgICApCmBgYAoKCgojIFNsZXV0aApgYGB7ciBpbmNsdWRlPUZBTFNFfQojIFNldHVwIHNsZXV0aCBvYmplY3QKc19vYmogPC0gc2xldXRoX3ByZXAoCiAgc2FtcGxlX3RvX2NvdmFyaWF0ZXMgPSByZW5hbWUoc2FtcGxlc19maWx0LHBhdGg9a2FsbGlzdG9fcGF0aCxzYW1wbGU9U1JSKSwKICBmdWxsX21vZGVsID0gflJlc3BvbnNlLAogIHRyYW5zZm9ybV9mdW5fY291bnRzPWZ1bmN0aW9uKHgpbG9nMih4KzEpLAogIHRhcmdldF9tYXBwaW5nID0gcmVuYW1lKHQyZyx0YXJnZXRfaWQ9ZW5zZW1ibF90cmFuc2NyaXB0X2lkX3ZlcnNpb24pICU+JQogICAgICBkaXN0aW5jdCh0YXJnZXRfaWQsZXh0ZXJuYWxfZ2VuZV9uYW1lKSwKICBhZ2dyZWdhdGlvbl9jb2x1bW4gPSAiZXh0ZXJuYWxfZ2VuZV9uYW1lIiwKICBnZW5lX21vZGU9VFJVRQopCmBgYAoKIyMgUENBIAoKIyMjIFNsZXV0aCBkZWZhdWx0CmBgYHtyfQpwbG90X3BjYShzX29iaix1bml0cz0ic2NhbGVkX3JlYWRzX3Blcl9iYXNlIixjb2xvcl9ieSA9ICJSZXNwb25zZSIsdXNlX2ZpbHRlcmVkID0gVFJVRSx0ZXh0X2xhYmVscyA9IEYpICsgZ2VvbV9sYWJlbChhZXMobGFiZWw9c2FtcGxlKSxzaXplPTIpCmBgYAoKIyMjIFN0YW5kYXJkaXplZCBQQ0EKYGBge3J9CgpzbGV1dGhfcm5hX25vcm0gPC0gc19vYmokb2JzX25vcm1fZmlsdCAlPiUgCiAgc2VsZWN0KHNhbXBsZSx0YXJnZXRfaWQsc2NhbGVkX3JlYWRzX3Blcl9iYXNlKSAlPiUKICBzcHJlYWQoa2V5PXRhcmdldF9pZCx2YWx1ZT1zY2FsZWRfcmVhZHNfcGVyX2Jhc2UpICU+JQogIGNvbHVtbl90b19yb3duYW1lcygic2FtcGxlIikKc2xldXRoX3N0ZF9wY2EgPC0gcHJjb21wKChzbGV1dGhfcm5hX25vcm0pLGNlbnRlcj1ULHNjYWxlLj1UKQpzbGV1dGhfdmFyaWFuY2VfZXhwbGFpbmVkID0gc3VtbWFyeShzbGV1dGhfc3RkX3BjYSkkaW1wb3J0YW5jZVsyLDE6Ml0Kc2xldXRoX3N0ZF9wY2EkeFssMToyXSAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgcm93bmFtZXNfdG9fY29sdW1uKCJTUlIiKSAlPiUKICBsZWZ0X2pvaW4oc2FtcGxlcyxieT0iU1JSIikgJT4lCiAgZ2dwbG90KGFlcyh4PVBDMSx5PVBDMixjb2xvcj1SZXNwb25zZSxsYWJlbD1TUlIpKSArCiAgICBnZW9tX3BvaW50KHNpemU9MykgKwogICAgbGFicyh0aXRsZT0iUENBIFBsb3Qgb2YgU2xldXRoIE5vcm1hbGl6ZWQgS2FsbGlzdG8gUXVhbnRpZmllZCBEYXRhIiwKICAgICAgICAgeCA9IHBhc3RlMCgiUEMxOiAiLCByb3VuZChzbGV1dGhfdmFyaWFuY2VfZXhwbGFpbmVkWzFdKjEwMCksIiUgVmFyaWFuY2UiKSwKICAgICAgICAgeSA9IHBhc3RlMCgiUEMyOiAiLCByb3VuZChzbGV1dGhfdmFyaWFuY2VfZXhwbGFpbmVkWzJdKjEwMCksIiUgVmFyaWFuY2UiKSkgKwogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1SRVNQT05TRV9DT0xPUlMpCmBgYAoKIyMgRGlmZmV4CgojIyMgTFJUCmBgYHtyfQpzX29iaiA8LSBzX29iaiAlPiUKICBzbGV1dGhfZml0KH5SZXNwb25zZSwiZnVsbCIpICU+JQogIHNsZXV0aF9maXQofjEsInJlZHVjZWQiKSAlPiUKICBzbGV1dGhfbHJ0KCJyZWR1Y2VkIiwiZnVsbCIpCmBgYApgYGB7cn0Kc2xldXRoX2RpZmZleF9scnQgPC0gc2xldXRoX3Jlc3VsdHMoc19vYmosdGVzdD0icmVkdWNlZDpmdWxsIix0ZXN0X3R5cGU9ImxydCIpICU+JSBmaWx0ZXIoIWlzLm5hKHB2YWwpKQpzbGV1dGhfZGlmZmV4X2xydCAlPiUgYXJyYW5nZShxdmFsKQpgYGAKCiMjIyBXYWxkCmBgYHtyfQpzX29iaiA8LSBzbGV1dGhfd3Qoc19vYmosIlJlc3BvbnNlUiIpCnNsZXV0aF9kaWZmZXhfd2FsZCA8LSBzbGV1dGhfcmVzdWx0cyhzX29iaix0ZXN0PSJSZXNwb25zZVIiLHRlc3RfdHlwZT0id2FsZCIpICAlPiUgZmlsdGVyKCFpcy5uYShwdmFsKSkKc2xldXRoX2RpZmZleF93YWxkICU+JSBhcnJhbmdlKHF2YWwpIApgYGAKYGBge3J9CnBsb3Rfdm9sY2FubyhzX29iaix0ZXN0X3R5cGU9Ind0Iix0ZXN0PSJSZXNwb25zZVIiLHNpZ19sZXZlbD0wLjA1KSArCiAgbGFicyh0aXRsZT0iVm9sY2FubyBQbG90IG9mIFNsZXV0aCBJZGVudGlmaWVkIERpZmZlcmVudGlhbGx5IEV4cHJlc3NlZCBHZW5lcyIsCiAgICAgICB4PSJsb2cyRkMiLCB5ID0gInF2YWwiLAogICAgICAgIGNvbG9yPSJRIDwgMC4wNSIpCmBgYAoKIyMgUUMKYGBge3J9Cm1lYW5TZFBsb3QobG9nMihzbGV1dGhfdG9fbWF0cml4KHNfb2JqLCJvYnNfbm9ybSIsInNjYWxlZF9yZWFkc19wZXJfYmFzZSIpICsgMSkscmFuaz1GQUxTRSxiaW5zPTEwMCkKYGBgCgojIERFU2VxIChLYWxsaXN0bykKYGBge3J9CmZpbGVzIDwtIGZpbGUucGF0aChzYW1wbGVzX2ZpbHQka2FsbGlzdG9fcGF0aCwiYWJ1bmRhbmNlLmg1IikKbmFtZXMoZmlsZXMpIDwtIHNhbXBsZXNfZmlsdCRgUGF0aWVudCBJRGAKdHhpIDwtIHR4aW1wb3J0KAogIGZpbGUucGF0aChzYW1wbGVzX2ZpbHQka2FsbGlzdG9fcGF0aCwiYWJ1bmRhbmNlLmg1IiksCiAgdHlwZSA9ICJrYWxsaXN0byIsCiAgdHgyZ2VuZSA9IHNlbGVjdCgKICAgIHQyZywKICAgIFRYTkFNRSA9IGVuc2VtYmxfdHJhbnNjcmlwdF9pZF92ZXJzaW9uLAogICAgR0VORUlEID0gZXh0ZXJuYWxfZ2VuZV9uYW1lCiAgKSwKICBpZ25vcmVBZnRlckJhciA9IFRSVUUsCikKcm93bmFtZXMoc2FtcGxlc19maWx0KSA8LSBzYW1wbGVzX2ZpbHQkYFBhdGllbnQgSURgCmRlc2VxX2thbGxpc3RvX2RhdGEgPC0gREVTZXFEYXRhU2V0RnJvbVR4aW1wb3J0KHR4aSxzYW1wbGVzX2ZpbHQsflJlc3BvbnNlKQprYWxsaXN0b19rZWVwIDwtIHJvd1N1bXMoY291bnRzKGRlc2VxX2thbGxpc3RvX2RhdGEpPjUpPjUKZGVzZXFfa2FsbGlzdG9fZGF0YSA8LSBkZXNlcV9rYWxsaXN0b19kYXRhW2thbGxpc3RvX2tlZXAsXQpgYGAKCiMjIFFDCmBgYHtyfQpkZXNlcV9rYWxsaXN0b19ub3JtIDwtIHZzdChkZXNlcV9rYWxsaXN0b19kYXRhKQptZWFuU2RQbG90KGFzc2F5KGRlc2VxX2thbGxpc3RvX25vcm0pLHJhbmtzPUZBTFNFLGJpbnM9MTAwKQoKYGBgCgoKIyMgUENBCmBgYHtyfQpwbG90UENBKGRlc2VxX2thbGxpc3RvX25vcm0saW50Z3JvdXA9IlJlc3BvbnNlIixudG9wPTUwMDAwMCkgICArCiAgbGFicyh0aXRsZT0iUENBIFBsb3Qgb2YgREVTZXEgTm9ybWFsaXplZCBLYWxsaXN0byBRdWFudGlmaWVkIERhdGEiLAogICAgICAgY29sb3I9IlJlc3BvbnNlIikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9UkVTUE9OU0VfQ09MT1JTKQpgYGAKIyMgRGlmZXgKYGBge3J9CmRlc2VxX2thbGxpc3RvX2RhdGEgPC0gREVTZXEoZGVzZXFfa2FsbGlzdG9fZGF0YSkKZGVzZXFfa2FsbGlzdG9fbHJ0IDwtIHJlc3VsdHMoZGVzZXFfa2FsbGlzdG9fZGF0YSxuYW1lPSJSZXNwb25zZV9SX3ZzX05SIix0aWR5PVRSVUUpCmRlc2VxX2thbGxpc3RvX2xydCAlPiUgCiAgI2ZpbHRlcihwYWRqIDwgMC4wNSkgJT4lCiAgYXJyYW5nZShwYWRqKSAlPiUKICBzZWxlY3Qocm93LGxvZzJGb2xkQ2hhbmdlLHB2YWx1ZSxwYWRqKSAlPiUKICBsZWZ0X2pvaW4oc2VsZWN0KHQyZyxlbnNlbWJsX2dlbmVfaWQsZXh0ZXJuYWxfZ2VuZV9uYW1lKSxieT1jKCJyb3ciPSJlbnNlbWJsX2dlbmVfaWQiKSkKYGBgCgojIERFU2VxIChGZWF0dXJlQ291bnRzKQpgYGB7cn0KZmNfbWF0IDwtIHJlYWRfdHN2KCIuLi93b3JrL2lucHV0L0dTRTEyNjA0NF9jb3VudHMudHh0IikKIGZjX21hdCA8LSBmY19tYXRbIWdyZXBsKCJeXFxkK1xcLSIsZmNfbWF0JFgxKSxdICU+JQogICBjb2x1bW5fdG9fcm93bmFtZXMoIlgxIikKIApmY19tYXRfZmlsdCA8LSAgZmNfbWF0ICU+JQogICBzZWxlY3Qob25lX29mKHNhbXBsZXNfZmlsdCRgUGF0aWVudCBJRGApKQpkZXNlcV9mYyA8LSBERVNlcURhdGFTZXRGcm9tTWF0cml4KGNvdW50RGF0YT1mY19tYXRfZmlsdCxjb2xEYXRhPXNhbXBsZXNfZmlsdCxkZXNpZ249flJlc3BvbnNlKQpmY19rZWVwIDwtIHJvd1N1bXMoY291bnRzKGRlc2VxX2ZjKT41KT41CmRlc2VxX2ZjIDwtIGRlc2VxX2ZjW2ZjX2tlZXAsXQpgYGAKCiMjIFFDCmBgYHtyfQpkZXNlcV9mY19ub3JtIDwtIHZzdChkZXNlcV9mYykKbWVhblNkUGxvdChhc3NheShkZXNlcV9mY19ub3JtKSxyYW5rPUZBTFNFLGJpbnM9MTAwKQpgYGAKCgojIyBQQ0EKYGBge3J9CnBsb3RQQ0EoZGVzZXFfZmNfbm9ybSxpbnRncm91cD0iUmVzcG9uc2UiLG50b3A9NTAwMDAwKSArCiAgbGFicyh0aXRsZT0iUENBIFBsb3Qgb2YgREVTZXEgTm9ybWFsaXplZCBGZWF0dXJlIENvdW50cy9TVEFSIFF1YW50aWZpZWQgRGF0YSIsCiAgICAgICBjb2xvcj0iUmVzcG9uc2UiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1SRVNQT05TRV9DT0xPUlMpCmBgYAoKIyMgRGlmZXgKYGBge3J9CmRlc2VxX2ZjIDwtIERFU2VxKGRlc2VxX2ZjKQpkZXNlcV9mY19scnQgPC0gcmVzdWx0cyhkZXNlcV9mYyxuYW1lPSJSZXNwb25zZV9SX3ZzX05SIix0aWR5PVRSVUUpCmRlc2VxX2ZjX2xydCAlPiUgCiAjIGZpbHRlcihwYWRqIDwgMC4wNSkgJT4lCiAgYXJyYW5nZShwYWRqKSAlPiUKICBzZWxlY3Qocm93LGxvZzJGb2xkQ2hhbmdlLHB2YWx1ZSxwYWRqKQpgYGAKCiMgTWVyZ2VkIEFuYWx5c2VzCgojIyBjb3VudHNfdG9fcnBtLCBwY2EKYGBge3J9CnRmYyA8LSB0KGZjX21hdF9maWx0KQojIENvbnZlcnQgS2FsbGlzdG8gY291bnRzIHRvIHJwbQprYWxyZWFkPC1jYmluZChrYWxsaXN0b19tYXBwZWRfcmVhZHM9c2FtcGxlc19maWx0JGthbGxpc3RvX21hcHBlZF9yZWFkcywgc2xldXRoX3JuYV9ub3JtICU+JSByb3duYW1lc190b19jb2x1bW4oInNhbXBsZSIpKQprYWxycG08LXNldERUKGthbHJlYWQpIFssIGxhcHBseSguU0QsIGZ1bmN0aW9uKHgpICh4ICogMWU2KSAvIChrYWxsaXN0b19tYXBwZWRfcmVhZHMpKSwgIGJ5PS4oa2FsbGlzdG9fbWFwcGVkX3JlYWRzLHNhbXBsZSldICU+JSAKICBjb2x1bW5fdG9fcm93bmFtZXMoInNhbXBsZSIpCgojIENvbnZlcnQgc3RhciBmYyB0byBycG0KI2dldCBkZiB3aXRoIGVzdCB0b3RhbCByZWFkcyBmb3Igc3RhcgpzdGFyZWFkPC1jYmluZChzdGFyX2VzdF9tYXBwZWQ9Y29sU3VtcyhmY19tYXRfZmlsdCksIGFzLmRhdGEuZnJhbWUodGZjKSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJzYW1wbGUiKSkKc3RhcnBtPC1zZXREVChzdGFyZWFkKSBbLCBsYXBwbHkoLlNELCBmdW5jdGlvbih4KSAoeCAqIDFlNikgLyAoc3Rhcl9lc3RfbWFwcGVkKSksICBieT0uKHN0YXJfZXN0X21hcHBlZCxzYW1wbGUpXSAlPiUKICBjb2x1bW5fdG9fcm93bmFtZXMoInNhbXBsZSIpCgojIyBzY2FsZSBwY2Egd2l0aCBycG0Ka2FsX3JwbV9wY2EgPC0gcHJjb21wKGthbHJwbVssLTFdLGNlbnRlcj1UUlVFLHNjYWxlLj1UUlVFKQoKc3Rhcm5vcm1fc3RkIDwtIHNjYWxlKHN0YXJwbVssLTFdKQojc3RhciBjb3VudHMgaW5jbHVkZSBzb21lIHplcm8sIHNldCBzY2FsZS9zdGQgYXBwbHkgdG8gdGhvc2UgIT0wCnN0YXJfcnBtX3BjYSA8LSBwcmNvbXAoc3Rhcm5vcm1fc3RkWyAsIHdoaWNoKGFwcGx5KHN0YXJub3JtX3N0ZCwgMiwgdmFyKSAhPSAwKV0sY2VudGVyPVRSVUUpCgojIyBzdXBlcmltcG9zZWQgcGNhIHBsb3RzIGZyb20gdGhlIHR3byBzb3VyY2VzCnAxIDwta2FsX3JwbV9wY2EkeFssMToyXSU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICByb3duYW1lc190b19jb2x1bW4oIlNSUiIpICU+JQogIGxlZnRfam9pbihzYW1wbGVzX2ZpbHQsYnk9IlNSUiIpCnAyPC1zdGFyX3JwbV9wY2EkeFssMToyXSAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgcm93bmFtZXNfdG9fY29sdW1uKCJQYXRpZW50IElEIikgJT4lCiAgbGVmdF9qb2luKHNhbXBsZXNfZmlsdCxieT0iUGF0aWVudCBJRCIpCmJpbmRfcm93cyhLYWxsaXN0bz1wMSxTVEFSPXAyLC5pZD0icXVhbnRfdHlwZSIpICU+JQogIGdncGxvdChhZXMoeD1QQzEseT1QQzIsY29sb3I9UmVzcG9uc2Usc2hhcGU9cXVhbnRfdHlwZSkpICsKICAgIGdlb21fcG9pbnQoc2l6ZT0zKSArCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPVJFU1BPTlNFX0NPTE9SUykgKyAKICAgIGxhYnModGl0bGU9IlN1cGVyaW1wb3NlZCBQQ0Egb2YgUlBNIE5vcm1hbGl6ZWQgQ291bnRzIiwKICAgICAgICAgc2hhcGU9Ik1ldGhvZCIpCmBgYAoKCgojIyBDb21wYXJpc29uIG9mIGxvZzJGQwpgYGB7cn0KZXF1YXRpb24gPSBmdW5jdGlvbih4KSB7CiAgbG1fY29lZiA8LSBsaXN0KGEgPSByb3VuZChhcy5udW1lcmljKGNvZWYoeClbMV0pLCBkaWdpdHMgPSAyKSwKICAgICAgICAgICAgICAgICAgYiA9IHJvdW5kKGFzLm51bWVyaWMoY29lZih4KVsyXSksIGRpZ2l0cyA9IDIpLAogICAgICAgICAgICAgICAgICByMiA9IHJvdW5kKHN1bW1hcnkoeCkkci5zcXVhcmVkLCBkaWdpdHMgPSAyKSk7CiAgbG1fZXEgPC0gc3Vic3RpdHV0ZShpdGFsaWMoeSkgPT0gYSArIGIgJS4lIGl0YWxpYyh4KSoiLCJ+fml0YWxpYyhSKV4yfiI9In5yMixsbV9jb2VmKQogIGFzLmNoYXJhY3Rlcihhcy5leHByZXNzaW9uKGxtX2VxKSk7ICAgICAgICAgICAgICAgICAKfQoKbG9nMmZjX2NvbWJpbmVkIDwtIHNsZXV0aF9kaWZmZXhfd2FsZCAlPiUKICBmaWx0ZXIoIWlzLm5hKGIpKSAlPiUKICBzZWxlY3QoZ2VuZT10YXJnZXRfaWQsc2xldXRoX2xvZzJGQz1iLHNsZXV0aF9xPXF2YWwpICU+JQogIGxlZnRfam9pbihzZWxlY3QoZGVzZXFfa2FsbGlzdG9fbHJ0LHJvdyxkZXNlcV9rYWxfbG9nMkZDPWxvZzJGb2xkQ2hhbmdlLGRlc2VxX2thbF9xPXBhZGopLGJ5PWMoImdlbmUiPSJyb3ciKSkgJT4lCiAgbGVmdF9qb2luKHNlbGVjdChkZXNlcV9mY19scnQscm93LGRlc2VxX2ZjX2xvZzJGQz1sb2cyRm9sZENoYW5nZSxkZXNlcV9mY19xPXBhZGopLGJ5PWMoImdlbmUiPSJyb3ciKSkgJT4lCiAgZmlsdGVyKCFpcy5uYShkZXNlcV9mY19sb2cyRkMpKQoKcmVncmVzc2lvbi5saW5lIDwtIGxtKGRlc2VxX2thbF9sb2cyRkMgfiBzbGV1dGhfbG9nMkZDLCBkYXRhID0gbG9nMmZjX2NvbWJpbmVkKQpsb2cyZmNfY29tYmluZWQgJT4lCiAgZ2dwbG90KGFlcyh4PXNsZXV0aF9sb2cyRkMseT1kZXNlcV9rYWxfbG9nMkZDKSkgKwogIGdlb21fcG9pbnQoYWVzKHg9c2xldXRoX2xvZzJGQyx5PWRlc2VxX2thbF9sb2cyRkMpKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTAsIGNvbG9yPSJkYXJrZ3JheSIpICsgCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PTAsIGNvbG9yPSJkYXJrZ3JheSIpICsKICB0aGVtZV9wdWJyKGJhc2Vfc2l6ZSA9IDE0KSsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBjb2xvcj0icmVkIikgKwogIGFubm90YXRlKCJ0ZXh0IiwgbGFiZWwgPSBlcXVhdGlvbihyZWdyZXNzaW9uLmxpbmUpLCB4ID0gLTMsIHkgPSA0LCBwYXJzZSA9IFRSVUUpKwogIGxhYnModGl0bGU9IkNvbXBhcmlzb24gb2YgbG9nMkZDIGJldHdlZW4gU2xldXRoIGFuZCBERVNlcSBLYWxsaXN0byIsCiAgICAgICB4PSJsb2cyIEZvbGQgQ2hhbmdlIChTbGV1dGgtS2FsbGlzdG8pIix5PSJsb2cyIEZvbGQgQ2hhbmdlIChERVNlcS1LYWxsaXN0bykiKQoKCnJlZ3Jlc3Npb24ubGluZSA8LSBsbShkZXNlcV9mY19sb2cyRkMgfiBkZXNlcV9rYWxfbG9nMkZDLCBkYXRhID0gbG9nMmZjX2NvbWJpbmVkKQpsb2cyZmNfY29tYmluZWQgJT4lCiAgZ2dwbG90KGFlcyh4PWRlc2VxX2thbF9sb2cyRkMseT1kZXNlcV9mY19sb2cyRkMpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MCwgY29sb3I9ImRhcmtncmF5IikgKyAKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9MCwgY29sb3I9ImRhcmtncmF5IikgKwogIHRoZW1lX3B1YnIoYmFzZV9zaXplID0gMTQpKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGNvbG9yPSJyZWQiKSArCiAgYW5ub3RhdGUoInRleHQiLCBsYWJlbCA9IGVxdWF0aW9uKHJlZ3Jlc3Npb24ubGluZSksIHggPSAtNCwgeSA9IDUsIHBhcnNlID0gVFJVRSkrCiAgbGFicyh0aXRsZT0iQ29tcGFyaXNvbiBvZiBsb2cyRkMgYmV0d2VlbiBERVNlcSBGcm9tIEthbGxpc3RvIGFuZCBGQyIsCiAgICAgICB4PSJsb2cyIEZvbGQgQ2hhbmdlIChERVNlcS1GZWF0dXJlQ291bnRzKSIseT0ibG9nMiBGb2xkIENoYW5nZSAoREVTZXEtS2FsbGlzdG8pIikKCmBgYAoKCgojIyBjb21iaW5lIGFsbCBsb2dGQyBkYXRhCmBgYHtyfQpzbGV1dGhfZGlmZmV4X3dhbGQgPC0gc2xldXRoX2RpZmZleF93YWxkICU+JSBmaWx0ZXIoIWlzLm5hKGIpKSAKZGVzZXFfZmNfbHJ0IDwtIGRlc2VxX2ZjX2xydCAlPiUgZmlsdGVyKCFpcy5uYShsb2cyRm9sZENoYW5nZSkpIApkZXNlcV9rYWxsaXN0b19scnQgPC0gZGVzZXFfa2FsbGlzdG9fbHJ0ICU+JSBmaWx0ZXIoIWlzLm5hKGxvZzJGb2xkQ2hhbmdlKSkgCgpsb2cyZmNfZnVsbF9jb21iaW5lZCA8LSBzbGV1dGhfZGlmZmV4X3dhbGQgJT4lCiAgc2VsZWN0KGdlbmU9dGFyZ2V0X2lkLHNsZXV0aF9sb2cyRkM9YixzbGV1dGhfcT1xdmFsKSAlPiUKICBmdWxsX2pvaW4oc2VsZWN0KGRlc2VxX2thbGxpc3RvX2xydCxyb3csZGVzZXFfa2FsX2xvZzJGQz1sb2cyRm9sZENoYW5nZSxkZXNlcV9rYWxfcT1wYWRqKSxieT1jKCJnZW5lIj0icm93IikpICU+JQogIGZ1bGxfam9pbihzZWxlY3QoZGVzZXFfZmNfbHJ0LHJvdyxkZXNlcV9mY19sb2cyRkM9bG9nMkZvbGRDaGFuZ2UsZGVzZXFfZmNfcT1wYWRqKSxieT1jKCJnZW5lIj0icm93IikpCmxvZzJmY19mdWxsX2NvbWJpbmVkCgpgYGAKCiMjIFZvbGNhbm8gUGxvdHMKCiMjIyBTbHVldGgKYGBge3J9Cgpsb2cyZmNfZnVsbF9jb21iaW5lZCRsb2cyRm9sZENoYW5nZSA8LSBsb2cyZmNfZnVsbF9jb21iaW5lZCRzbGV1dGhfbG9nMkZDCmxvZzJmY19mdWxsX2NvbWJpbmVkJHBhZGogPC0gbG9nMmZjX2Z1bGxfY29tYmluZWQkc2xldXRoX3EKbG9nMmZjX2ZpbmFsIDwtIGxvZzJmY19mdWxsX2NvbWJpbmVkICU+JSBmaWx0ZXIoIWlzLm5hKGxvZzJGb2xkQ2hhbmdlKSkgCgp0aHJlcyA8LSAxCmtleXZhbHMuY29sb3IgPC0gcmVwKCdncmVlbicsIG5yb3cobG9nMmZjX2ZpbmFsKSkKIyBzZXQgdGhlIGJhc2UgbmFtZS9sYWJlbCBhcyAnTWlkJwpuYW1lcyhrZXl2YWxzLmNvbG9yKSA8LSByZXAoJ01pZCcsIG5yb3cobG9nMmZjX2ZpbmFsKSkKCmtleXZhbHMuY29sb3Jbd2hpY2gobG9nMmZjX2ZpbmFsJGxvZzJGb2xkQ2hhbmdlID4gdGhyZXMpXSA8LSAncmVkJwpuYW1lcyhrZXl2YWxzLmNvbG9yKVt3aGljaChsb2cyZmNfZmluYWwkbG9nMkZvbGRDaGFuZ2UgPiB0aHJlcyldIDwtICdVcFJlZ3VsYXRlZCcKCmtleXZhbHMuY29sb3Jbd2hpY2gobG9nMmZjX2ZpbmFsJGxvZzJGb2xkQ2hhbmdlIDwgLXRocmVzKV0gPC0gJ3JveWFsYmx1ZScKbmFtZXMoa2V5dmFscy5jb2xvcilbd2hpY2gobG9nMmZjX2ZpbmFsJGxvZzJGb2xkQ2hhbmdlIDwgLXRocmVzKV0gPC0gJ0Rvd25SZWd1bGF0ZWQnCgpkb3duLmdlbmVzICA8LSB3aGljaCgobG9nMmZjX2ZpbmFsJHBhZGogPCAwLjMpICYgKGxvZzJmY19maW5hbCRsb2cyRm9sZENoYW5nZSA8PSAtMykpCnVwLmdlbmVzICA8LSB3aGljaCgobG9nMmZjX2ZpbmFsJHBhZGogPCAwLjMpICYgKGxvZzJmY19maW5hbCRsb2cyRm9sZENoYW5nZSA+PSAzKSkKdXAuZG93bi5nZW5lcyA9IGMoZG93bi5nZW5lcywgdXAuZ2VuZXMpCmxlbmd0aCh1cC5kb3duLmdlbmVzKQoKdGFibGUoa2V5dmFscy5jb2xvcikKRW5oYW5jZWRWb2xjYW5vKGxvZzJmY19maW5hbCwKICAgICAgICAgICAgICAgIGxhYiA9IChsb2cyZmNfZmluYWwkZ2VuZSksCiAgICAgICAgICAgICAgICB4ID0gJ2xvZzJGb2xkQ2hhbmdlJywKICAgICAgICAgICAgICAgIHkgPSAncGFkaicsIAogICAgICAgICAgICAgICAgdGl0bGUgPSAnUiB2cy4gTlIgIC0gc2xldXRoJywKICAgICAgICAgICAgICAgIGRyYXdDb25uZWN0b3JzID0gVFJVRSwKICAgICAgICAgICAgICAgIHdpZHRoQ29ubmVjdG9ycyA9IDAuMywKICAgICAgICAgICAgICAgIGNvbENvbm5lY3RvcnMgPSAnZ3JleTMwJywKICAgICAgICAgICAgICAgIGNvbEN1c3RvbSA9IGtleXZhbHMuY29sb3IsCiAgICAgICAgICAgICAgICBwQ3V0b2ZmID0gTkEsCiAgICAgICAgICAgICAgICBjdXRvZmZMaW5lVHlwZSA9ICdibGFuaycsCiAgICAgICAgICAgICAgICB2bGluZSA9IGMoLXRocmVzLHRocmVzKSwKICAgICAgICAgICAgICAgIHZsaW5lQ29sID0gYygnZ3JleTUwJywgJ2dyZXk1MCcpLAogICAgICAgICAgICAgICAgZ3JpZGxpbmVzLm1ham9yID0gRkFMU0UsCiAgICAgICAgICAgICAgICBncmlkbGluZXMubWlub3IgPSBGQUxTRSwKICAgICAgICAgICAgICAgIHlsaW0gPSBjKDAsMS41KSwKICAgICAgICAgICAgICAgIHNlbGVjdExhYiA9IGxvZzJmY19maW5hbCRnZW5lW3VwLmRvd24uZ2VuZXNdCiAgICAgICAgICAgICAgICApCgpgYGAKCiMjIyBkZXNlcV9rYWwKYGBge3J9Cgpsb2cyZmNfZnVsbF9jb21iaW5lZCRsb2cyRm9sZENoYW5nZSA8LSBsb2cyZmNfZnVsbF9jb21iaW5lZCRkZXNlcV9rYWxfbG9nMkZDCmxvZzJmY19mdWxsX2NvbWJpbmVkJHBhZGogPC0gbG9nMmZjX2Z1bGxfY29tYmluZWQkZGVzZXFfa2FsX3EKbG9nMmZjX2ZpbmFsIDwtIGxvZzJmY19mdWxsX2NvbWJpbmVkICU+JSBmaWx0ZXIoIWlzLm5hKGxvZzJGb2xkQ2hhbmdlKSkgCgp0aHJlcyA8LSAxCmtleXZhbHMuY29sb3IgPC0gcmVwKCdncmVlbicsIG5yb3cobG9nMmZjX2ZpbmFsKSkKIyBzZXQgdGhlIGJhc2UgbmFtZS9sYWJlbCBhcyAnTWlkJwpuYW1lcyhrZXl2YWxzLmNvbG9yKSA8LSByZXAoJ01pZCcsIG5yb3cobG9nMmZjX2ZpbmFsKSkKCmtleXZhbHMuY29sb3Jbd2hpY2gobG9nMmZjX2ZpbmFsJGxvZzJGb2xkQ2hhbmdlID4gdGhyZXMpXSA8LSAncmVkJwpuYW1lcyhrZXl2YWxzLmNvbG9yKVt3aGljaChsb2cyZmNfZmluYWwkbG9nMkZvbGRDaGFuZ2UgPiB0aHJlcyldIDwtICdVcFJlZ3VsYXRlZCcKCmtleXZhbHMuY29sb3Jbd2hpY2gobG9nMmZjX2ZpbmFsJGxvZzJGb2xkQ2hhbmdlIDwgLXRocmVzKV0gPC0gJ3JveWFsYmx1ZScKbmFtZXMoa2V5dmFscy5jb2xvcilbd2hpY2gobG9nMmZjX2ZpbmFsJGxvZzJGb2xkQ2hhbmdlIDwgLXRocmVzKV0gPC0gJ0Rvd25SZWd1bGF0ZWQnCgpkb3duLmdlbmVzICA8LSB3aGljaCgobG9nMmZjX2ZpbmFsJHBhZGogPCAwLjMpICYgKGxvZzJmY19maW5hbCRsb2cyRm9sZENoYW5nZSA8PSAtMjApKQp1cC5nZW5lcyAgPC0gd2hpY2goKGxvZzJmY19maW5hbCRwYWRqIDwgMC4zKSAmIChsb2cyZmNfZmluYWwkbG9nMkZvbGRDaGFuZ2UgPj0gNSkpCnVwLmRvd24uZ2VuZXMgPSBjKGRvd24uZ2VuZXMsIHVwLmdlbmVzKQpsZW5ndGgodXAuZG93bi5nZW5lcykKCnRhYmxlKGtleXZhbHMuY29sb3IpCkVuaGFuY2VkVm9sY2Fubyhsb2cyZmNfZmluYWwsCiAgICAgICAgICAgICAgICBsYWIgPSAobG9nMmZjX2ZpbmFsJGdlbmUpLAogICAgICAgICAgICAgICAgeCA9ICdsb2cyRm9sZENoYW5nZScsCiAgICAgICAgICAgICAgICB5ID0gJ3BhZGonLCAKICAgICAgICAgICAgICAgIHRpdGxlID0gJ1IgdnMuIE5SICAtIERlc2VxLWthbGxpc3RvJywKICAgICAgICAgICAgICAgIGRyYXdDb25uZWN0b3JzID0gVFJVRSwKICAgICAgICAgICAgICAgIHdpZHRoQ29ubmVjdG9ycyA9IDAuMywKICAgICAgICAgICAgICAgIGNvbENvbm5lY3RvcnMgPSAnZ3JleTMwJywKICAgICAgICAgICAgICAgIGNvbEN1c3RvbSA9IGtleXZhbHMuY29sb3IsCiAgICAgICAgICAgICAgICBwQ3V0b2ZmID0gTkEsCiAgICAgICAgICAgICAgICBjdXRvZmZMaW5lVHlwZSA9ICdibGFuaycsCiAgICAgICAgICAgICAgICB2bGluZSA9IGMoLXRocmVzLHRocmVzKSwKICAgICAgICAgICAgICAgIHZsaW5lQ29sID0gYygnZ3JleTUwJywgJ2dyZXk1MCcpLAogICAgICAgICAgICAgICAgZ3JpZGxpbmVzLm1ham9yID0gRkFMU0UsCiAgICAgICAgICAgICAgICBncmlkbGluZXMubWlub3IgPSBGQUxTRSwKICAgICAgICAgICAgICAgIHlsaW0gPSBjKDAsMTApLAogICAgICAgICAgICAgICAgc2VsZWN0TGFiID0gbG9nMmZjX2ZpbmFsJGdlbmVbdXAuZG93bi5nZW5lc10KICAgICAgICAgICAgICAgICkKCmBgYAoKCiMjIyBkZXNlcV9mYwpgYGB7cn0KbG9nMmZjX2Z1bGxfY29tYmluZWQkbG9nMkZvbGRDaGFuZ2UgPC0gbG9nMmZjX2Z1bGxfY29tYmluZWQkZGVzZXFfZmNfbG9nMkZDCmxvZzJmY19mdWxsX2NvbWJpbmVkJHBhZGogPC0gbG9nMmZjX2Z1bGxfY29tYmluZWQkZGVzZXFfZmNfcQpsb2cyZmNfZmluYWwgPC0gbG9nMmZjX2Z1bGxfY29tYmluZWQgJT4lIGZpbHRlcighaXMubmEobG9nMkZvbGRDaGFuZ2UpKSAKCnRocmVzIDwtIDEKa2V5dmFscy5jb2xvciA8LSByZXAoJ2dyZWVuJywgbnJvdyhsb2cyZmNfZmluYWwpKQojIHNldCB0aGUgYmFzZSBuYW1lL2xhYmVsIGFzICdNaWQnCm5hbWVzKGtleXZhbHMuY29sb3IpIDwtIHJlcCgnTWlkJywgbnJvdyhsb2cyZmNfZmluYWwpKQoKa2V5dmFscy5jb2xvclt3aGljaChsb2cyZmNfZmluYWwkbG9nMkZvbGRDaGFuZ2UgPiB0aHJlcyldIDwtICdyZWQnCm5hbWVzKGtleXZhbHMuY29sb3IpW3doaWNoKGxvZzJmY19maW5hbCRsb2cyRm9sZENoYW5nZSA+IHRocmVzKV0gPC0gJ1VwUmVndWxhdGVkJwoKa2V5dmFscy5jb2xvclt3aGljaChsb2cyZmNfZmluYWwkbG9nMkZvbGRDaGFuZ2UgPCAtdGhyZXMpXSA8LSAncm95YWxibHVlJwpuYW1lcyhrZXl2YWxzLmNvbG9yKVt3aGljaChsb2cyZmNfZmluYWwkbG9nMkZvbGRDaGFuZ2UgPCAtdGhyZXMpXSA8LSAnRG93blJlZ3VsYXRlZCcKCmRvd24uZ2VuZXMgIDwtIHdoaWNoKChsb2cyZmNfZmluYWwkcGFkaiA8IDAuMykgJiAobG9nMmZjX2ZpbmFsJGxvZzJGb2xkQ2hhbmdlIDw9IC04KSkKdXAuZ2VuZXMgIDwtIHdoaWNoKChsb2cyZmNfZmluYWwkcGFkaiA8IDAuMykgJiAobG9nMmZjX2ZpbmFsJGxvZzJGb2xkQ2hhbmdlID49IDQpKQp1cC5kb3duLmdlbmVzID0gYyhkb3duLmdlbmVzLCB1cC5nZW5lcykKbGVuZ3RoKHVwLmRvd24uZ2VuZXMpCgp0YWJsZShrZXl2YWxzLmNvbG9yKQpFbmhhbmNlZFZvbGNhbm8obG9nMmZjX2ZpbmFsLAogICAgICAgICAgICAgICAgbGFiID0gKGxvZzJmY19maW5hbCRnZW5lKSwKICAgICAgICAgICAgICAgIHggPSAnbG9nMkZvbGRDaGFuZ2UnLAogICAgICAgICAgICAgICAgeSA9ICdwYWRqJywgCiAgICAgICAgICAgICAgICB0aXRsZSA9ICdSIHZzLiBOUiAgLSBEZXNlcS1mYycsCiAgICAgICAgICAgICAgICBkcmF3Q29ubmVjdG9ycyA9IFRSVUUsCiAgICAgICAgICAgICAgICB3aWR0aENvbm5lY3RvcnMgPSAwLjMsCiAgICAgICAgICAgICAgICBjb2xDb25uZWN0b3JzID0gJ2dyZXkzMCcsCiAgICAgICAgICAgICAgICBjb2xDdXN0b20gPSBrZXl2YWxzLmNvbG9yLAogICAgICAgICAgICAgICAgcEN1dG9mZiA9IE5BLAogICAgICAgICAgICAgICAgY3V0b2ZmTGluZVR5cGUgPSAnYmxhbmsnLAogICAgICAgICAgICAgICAgdmxpbmUgPSBjKC10aHJlcyx0aHJlcyksCiAgICAgICAgICAgICAgICB2bGluZUNvbCA9IGMoJ2dyZXk1MCcsICdncmV5NTAnKSwKICAgICAgICAgICAgICAgIGdyaWRsaW5lcy5tYWpvciA9IEZBTFNFLAogICAgICAgICAgICAgICAgZ3JpZGxpbmVzLm1pbm9yID0gRkFMU0UsCiAgICAgICAgICAgICAgICB5bGltID0gYygwLDE1KSwKICAgICAgICAgICAgICAgIHNlbGVjdExhYiA9IGxvZzJmY19maW5hbCRnZW5lW3VwLmRvd24uZ2VuZXNdCiAgICAgICAgICAgICAgICApCgpgYGAKCgojIyBWZW5uIGRpYWdyYW0KYGBge3J9CnRocmVzIDwtIDEKIyBnZXQgdGhlIG5hbWUgb2YgdGhlIHVwcmVndWxhdGVkL2Rvd25yZWd1bGF0ZWQgZ2VuZXMgZnJvbSAzIGRpZmZlcmVudCBtZXRob2RzCnNsZXV0aC51cCA8LSBzbGV1dGhfZGlmZmV4X3dhbGQkdGFyZ2V0X2lkW3doaWNoKChzbGV1dGhfZGlmZmV4X3dhbGQkcXZhbCA8PSAwLjA1KSAmIChzbGV1dGhfZGlmZmV4X3dhbGQkYiA+PSB0aHJlcykpXQpzbGV1dGguZG93biA8LSBzbGV1dGhfZGlmZmV4X3dhbGQkdGFyZ2V0X2lkW3doaWNoKChzbGV1dGhfZGlmZmV4X3dhbGQkcXZhbCA8PSAwLjA1KSAmIChzbGV1dGhfZGlmZmV4X3dhbGQkYiA8PSAtdGhyZXMpKV0KZGVzZXEuay51cCA8LSBkZXNlcV9rYWxsaXN0b19scnQkcm93W3doaWNoKChkZXNlcV9rYWxsaXN0b19scnQkcGFkaiA8PSAwLjA1KSAmIChkZXNlcV9rYWxsaXN0b19scnQkbG9nMkZvbGRDaGFuZ2UgPj0gdGhyZXMpKV0KZGVzZXEuay5kb3duIDwtIGRlc2VxX2thbGxpc3RvX2xydCRyb3dbd2hpY2goKGRlc2VxX2thbGxpc3RvX2xydCRwYWRqIDw9IDAuMDUpICYgKGRlc2VxX2thbGxpc3RvX2xydCRsb2cyRm9sZENoYW5nZSA8PSAtdGhyZXMpKV0KZGVzZXEuZmMudXAgPC0gZGVzZXFfZmNfbHJ0JHJvd1t3aGljaCgoZGVzZXFfZmNfbHJ0JHBhZGogPD0gMC4wNSkgJiAoZGVzZXFfZmNfbHJ0JGxvZzJGb2xkQ2hhbmdlID49IHRocmVzKSldCmRlc2VxLmZjLmRvd24gPC0gZGVzZXFfZmNfbHJ0JHJvd1t3aGljaCgoZGVzZXFfZmNfbHJ0JHBhZGogPD0gMC4wNSkgJiAoZGVzZXFfZmNfbHJ0JGxvZzJGb2xkQ2hhbmdlIDw9IC10aHJlcykpXQoKIyB1cC1yZWd1bGF0ZWQgdmVubi1kaWFncmFtCmdyaWQubmV3cGFnZSgpCnZkIDwtIHZlbm4uZGlhZ3JhbSh4ID0gbGlzdCgiU2xldXRoLUthbGxpc3RvIiA9IChzbGV1dGgudXApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRFU2VxLUthbGxpc3RvIiA9IChkZXNlcS5rLnVwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJERVNlcS1GZWF0dXJlQ291bnRzIiA9IChkZXNlcS5mYy51cCkpLAogICAgICAgICAgICAgICAgICAgIG1haW49IlZlbm4gRGlhZ3JhbSBvZiBVcHJlZ3VsYXRlZCBHZW5lcyBCZXR3ZWVuIDMgTWV0aG9kcyIsCiAgICAgICAgICAgICAgICAgICBmaWxsID0gYnJld2VyLnBhbCg0LCAiU2V0MiIpWzE6M10sIGZpbGVuYW1lID0gTlVMTCkKZ3JpZC5kcmF3KHZkKQoKIyBkb3duLXJlZ3VsYXRlZCB2ZW5uLWRpYWdyYW0KZ3JpZC5uZXdwYWdlKCkKdmQgPC0gdmVubi5kaWFncmFtKHggPSBsaXN0KCJTbGV1dGgtS2FsbGlzdG8iID0gKHNsZXV0aC5kb3duKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJERVNlcS1LYWxsaXN0byIgPSAoZGVzZXEuay5kb3duKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJERVNlcS1GZWF0dXJlQ291bnRzIiA9IChkZXNlcS5mYy5kb3duKSksCiAgICAgICAgICAgICAgICAgICBtYWluPSJWZW5uIERpYWdyYW0gb2YgRG93bnJlZ3VsYXRlZCBHZW5lcyBCZXR3ZWVuIDMgTWV0aG9kcyIsCiAgICAgICAgICAgICAgICAgICBmaWxsID0gYnJld2VyLnBhbCg0LCAiU2V0MiIpWzE6M10sIGZpbGVuYW1lID0gTlVMTCkKZ3JpZC5kcmF3KHZkKQoKYGBgCgoKIyMgSGVhdG1hcApgYGB7cn0KCiNzbGV1dGgudXAgPC0gc2xldXRoX2RpZmZleF93YWxkJHRhcmdldF9pZFt3aGljaCgoc2xldXRoX2RpZmZleF93YWxkJHF2YWwgPD0gMC4wNSkgJiAoc2xldXRoX2RpZmZleF93YWxkJGIgPj0gdGhyZXMpKV0KI3NsZXV0aC5kb3duIDwtIHNsZXV0aF9kaWZmZXhfd2FsZCR0YXJnZXRfaWRbd2hpY2goKHNsZXV0aF9kaWZmZXhfd2FsZCRxdmFsIDw9IDAuMDUpICYgKHNsZXV0aF9kaWZmZXhfd2FsZCRiIDw9IC10aHJlcykpXQoKbm9ybWFsaXplZF9jb3VudHMgPC0gY291bnRzKGRlc2VxX2thbGxpc3RvX2RhdGEsIG5vcm1hbGl6ZWQ9VFJVRSkKY29sbmFtZXMobm9ybWFsaXplZF9jb3VudHMpCgoKdXBkb3duLmdlbmUgPC0gZGVzZXFfa2FsbGlzdG9fbHJ0W3doaWNoKChkZXNlcV9rYWxsaXN0b19scnQkbG9nMkZvbGRDaGFuZ2UgPiAxIHwgZGVzZXFfa2FsbGlzdG9fbHJ0JGxvZzJGb2xkQ2hhbmdlIDwgLTEpICYgZGVzZXFfa2FsbGlzdG9fbHJ0JHBhZGogPCAwLjA1KSxdCnVwZG93bi5nZW5lJHVwZG93biA8LSBpZmVsc2UoKHVwZG93bi5nZW5lJGxvZzJGb2xkQ2hhbmdlPiAxKSwgInVwIiwgImRvd24iKQoKdXBkb3duLnJlZ3VsIDwtIGFzLm1hdHJpeCh1cGRvd24uZ2VuZVssInVwZG93biIsIGRyb3A9RkFMU0VdKQoKaGVhdG1hcGRhdGEgPC0gbm9ybWFsaXplZF9jb3VudHNbbWF0Y2godXBkb3duLmdlbmUkcm93LCByb3cubmFtZXMobm9ybWFsaXplZF9jb3VudHMpKSxdCgp6c2NvcmVfZGF0YSA8LSB0KGFwcGx5KGhlYXRtYXBkYXRhLCAxLCBzY2FsZSkpCmNvbG5hbWVzKHpzY29yZV9kYXRhKSA9IGNvbG5hbWVzKGhlYXRtYXBkYXRhKQoKY29sX3VwZG93biA9IGMoInVwIiA9ICJwaW5rIiwgImRvd24iID0gInB1cnBsZSIpCgpyb3cubmFtZXMoc2FtcGxlc19maWx0KSA8LSBzYW1wbGVzX2ZpbHQkYFBhdGllbnQgSURgCnNhbXBsZXNfZmlsdAoKaGEgPSBIZWF0bWFwQW5ub3RhdGlvbih0eXBlID0gc2FtcGxlc19maWx0JFJlc3BvbnNlLCBhbm5vdGF0aW9uX25hbWVfc2lkZSA9ICJsZWZ0IiwgIAogICAgICAgICAgICAgICAgICAgICAgIGNvbCA9IGxpc3QodHlwZSA9IGMoIk5SIiA9ICJ5ZWxsb3ciLCAiUiIgPSAiYmxhY2siKSkpCmRpbShub3JtYWxpemVkX2NvdW50cykKCmh0MSA9IEhlYXRtYXAoenNjb3JlX2RhdGEsIG5hbWUgPSAiZXhwcmVzc2lvbiIsCiAgICAgICAgY29sID0gY29sb3JSYW1wMihjKC0yLCAwLCAyKSwgYygiYmx1ZSIsICJ3aGl0ZSIsICJyZWQiKSksCiAgICAgICAgc2hvd19jb2x1bW5fbmFtZXMgPSBGQUxTRSwgc2hvd19yb3dfbmFtZXM9RkFMU0UsCiAgICAgICAgcm93X3RpdGxlID0gTlVMTCwKICAgICAgICBib3R0b21fYW5ub3RhdGlvbiA9IGhhLAogICAgICAgIGhlYXRtYXBfd2lkdGggPSA0MDAsIGhlYXRtYXBfaGVpZ2h0ID0gMTAwKQoKaHQyID0gSGVhdG1hcCh1cGRvd24ucmVndWwsIG5hbWUgPSAidXAgdnMgZG93biIsIGNvbCA9IGNvbF91cGRvd24sIHNob3dfcm93X25hbWVzPUZBTFNFKQpodF9saXN0ID0gaHQxICsgaHQyCmRyYXcoaHRfbGlzdCwgcm93X2ttID0gMSwgcm93X3NwbGl0ID0gdXBkb3duLnJlZ3VsLCBjbHVzdGVyX3Jvd3MgPSBUUlVFKQpgYGAKCmBgYHtyfQoKI3NsZXV0aC51cCA8LSBzbGV1dGhfZGlmZmV4X3dhbGQkdGFyZ2V0X2lkW3doaWNoKChzbGV1dGhfZGlmZmV4X3dhbGQkcXZhbCA8PSAwLjA1KSAmIChzbGV1dGhfZGlmZmV4X3dhbGQkYiA+PSB0aHJlcykpXQojc2xldXRoLmRvd24gPC0gc2xldXRoX2RpZmZleF93YWxkJHRhcmdldF9pZFt3aGljaCgoc2xldXRoX2RpZmZleF93YWxkJHF2YWwgPD0gMC4wNSkgJiAoc2xldXRoX2RpZmZleF93YWxkJGIgPD0gLXRocmVzKSldCm5vcm1hbGl6ZWRfY291bnRzIDwtIGNvdW50cyhkZXNlcV9mYywgbm9ybWFsaXplZD1UUlVFKQoKdXBkb3duLmdlbmUgPC0gZGVzZXFfZmNfbHJ0W3doaWNoKChkZXNlcV9mY19scnQkbG9nMkZvbGRDaGFuZ2UgPiAxIHwgZGVzZXFfZmNfbHJ0JGxvZzJGb2xkQ2hhbmdlIDwgLTEpICYgZGVzZXFfZmNfbHJ0JHBhZGogPCAwLjA1KSxdCnVwZG93bi5nZW5lJHVwZG93biA8LSBpZmVsc2UoKHVwZG93bi5nZW5lJGxvZzJGb2xkQ2hhbmdlPiAxKSwgInVwIiwgImRvd24iKQoKdXBkb3duLnJlZ3VsIDwtIGFzLm1hdHJpeCh1cGRvd24uZ2VuZVssInVwZG93biIsIGRyb3A9RkFMU0VdKQoKaGVhdG1hcGRhdGEgPC0gbm9ybWFsaXplZF9jb3VudHNbbWF0Y2godXBkb3duLmdlbmUkcm93LCByb3cubmFtZXMobm9ybWFsaXplZF9jb3VudHMpKSxdCgp6c2NvcmVfZGF0YSA8LSB0KGFwcGx5KGhlYXRtYXBkYXRhLCAxLCBzY2FsZSkpCmNvbG5hbWVzKHpzY29yZV9kYXRhKSA9IGNvbG5hbWVzKGhlYXRtYXBkYXRhKQoKY29sX3VwZG93biA9IGMoInVwIiA9ICJwaW5rIiwgImRvd24iID0gInB1cnBsZSIpCgpyb3cubmFtZXMoc2FtcGxlc19maWx0KSA8LSBzYW1wbGVzX2ZpbHQkYFBhdGllbnQgSURgCnNhbXBsZXNfZmlsdAoKaGEgPSBIZWF0bWFwQW5ub3RhdGlvbih0eXBlID0gc2FtcGxlc19maWx0JFJlc3BvbnNlLCBhbm5vdGF0aW9uX25hbWVfc2lkZSA9ICJsZWZ0IiwgIAogICAgICAgICAgICAgICAgICAgICAgIGNvbCA9IGxpc3QodHlwZSA9IGMoIk5SIiA9ICJ5ZWxsb3ciLCAiUiIgPSAiYmxhY2siKSkpCmRpbShub3JtYWxpemVkX2NvdW50cykKCmh0MSA9IEhlYXRtYXAoenNjb3JlX2RhdGEsIG5hbWUgPSAiZXhwcmVzc2lvbiIsCiAgICAgICAgY29sID0gY29sb3JSYW1wMihjKC0yLCAwLCAyKSwgYygiYmx1ZSIsICJ3aGl0ZSIsICJyZWQiKSksCiAgICAgICAgc2hvd19jb2x1bW5fbmFtZXMgPSBGQUxTRSwgc2hvd19yb3dfbmFtZXM9RkFMU0UsCiAgICAgICAgcm93X3RpdGxlID0gTlVMTCwKICAgICAgICBib3R0b21fYW5ub3RhdGlvbiA9IGhhLAogICAgICAgIGhlYXRtYXBfd2lkdGggPSA0MDAsIGhlYXRtYXBfaGVpZ2h0ID0gMTAwKQoKaHQyID0gSGVhdG1hcCh1cGRvd24ucmVndWwsIG5hbWUgPSAidXAgdnMgZG93biIsIGNvbCA9IGNvbF91cGRvd24sIHNob3dfcm93X25hbWVzPUZBTFNFKQpodF9saXN0ID0gaHQxICsgaHQyCmRyYXcoaHRfbGlzdCwgcm93X2ttID0gMSwgcm93X3NwbGl0ID0gdXBkb3duLnJlZ3VsLCBjbHVzdGVyX3Jvd3MgPSBUUlVFKQpgYGAKCiMgU2FtcGxlIG92ZXJ2aWV3IHRhYmxlCmBgYHtyfQpzYW1wbGVfc3VtbWFyeSA8LSBkYXRhLmZyYW1lKGZjX21hcHBlZF9yZWFkcz1jb2xTdW1zKGZjX21hdCkpICU+JSAKICByb3duYW1lc190b19jb2x1bW4oIlBhdGllbnQgSUQiKSAlPiUgCiAgbGVmdF9qb2luKHNhbXBsZXMsYnk9IlBhdGllbnQgSUQiKSAlPiUKICBzZWxlY3QoYFBhdGllbnQgSURgLFNSUixSZXNwb25zZSwiU2VxdWVuY2VkIFJlYWRzIj1uX3JlYWRzLCJNYXBwZWQgUmVhZHMoS2FsbGlzdG8pIj1rYWxsaXN0b19tYXBwZWRfcmVhZHMsIk1hcHBlZCBSZWFkcyhTVEFSL0ZlYXR1cmVDb3VudHMpIj1mY19tYXBwZWRfcmVhZHMpCnNhbXBsZV9zdW1tYXJ5CmBgYAoKCiMgV3JpdGUgb3V0IHJlbGV2YW50IGZpbGVzCmBgYHtyfQpzbGV1dGhfZGlmZmV4X2xydCAlPiUgd3JpdGVfdHN2KCJzbGV1dGhfZGlmZmV4X2xydC50c3YiKQpzbGV1dGhfZGlmZmV4X3dhbGQgJT4lIHdyaXRlX3Rzdigic2xldXRoX2RpZmZleF93YWxkLnRzdiIpCmRlc2VxX2thbGxpc3RvX2xydCAlPiUgd3JpdGVfdHN2KCJkZXNlcV9rYWxsaXN0b19scnQudHN2IikKZGVzZXFfZmNfbHJ0ICU+JSB3cml0ZV90c3YoImRlc2VxX2ZjX2xydC50c3YiKQpzYW1wbGVfc3VtbWFyeSU+JSB3cml0ZV90c3YoInNhbXBsZV9zdW1tYXJ5LnRzdiIpCmBgYAo=